<?php
// +----------------------------------------------------------------------
// | ThinkPHP [WE CAN DO IT JUST THINK]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------

namespace app\helper;

use Elasticsearch\ClientBuilder;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Elasticsearch\ConnectionPool\StaticNoPingConnectionPool;
use Elasticsearch\ConnectionPool\Selectors\StickyRoundRobinSelector;
// use Elasticsearch\Common\Exceptions\MaxRetriesException;
// use Elasticsearch\Common\Exceptions\TransportException;

/**
 *
 * 要求安装libcurl扩展：https://github.com/nicolasff/phpredis
 */
class Elastic
{
    public $client;

    public function __construct()
    {
        $logger = new Logger('name'); // 日志
        $logger->pushHandler(new StreamHandler('path/to/your.log', Logger::WARNING));
        // $defaultHandler = ClientBuilder::defaultHandler();
        // $defaultHandler = new SwooleHandler();
        $this->client = ClientBuilder::create()           // Instantiate a new ClientBuilder
            ->setHosts(config('database.connections.ecs'))      // Set the hosts
            // ->setHandler($defaultHandler)
            ->setRetries(2)  // 重连次数
            ->setConnectionPool(StaticNoPingConnectionPool::class)  // 连接池
            ->setSelector(StickyRoundRobinSelector::class)  // 连接池选择器
            ->build();              // Build the client object
    }
    public function exists(string $index){
        return $this->client->indices()->exists(['index' => [$index]]);
    }
    public function create(string $index, array $body = []){
        $params = [
            'index' => $index,
            'body' => [
                'settings' => [ // 配置
                    'number_of_shards' => 1,   // 分片1 一个分片是一个实例
                    'number_of_replicas' => 0, // 副本0，因为只有一台ES
                    /*
                    'reuters'=> [   // 自定义分析器,全文搜索时使用
                        'char_filter'=> [  // 字符过滤器
                            '&_to_and'=> [
                                'type' => 'mapping',
                                'mappings'=> ['&=> and ']
                        ]],
                        'filter'=> [  // 词干过滤器
                            'my_stopwords'=> [
                                'type' => 'stop',
                                'stopwords'=> ['the', 'a']
                        ]],
                        'analyzer'=> [  // 分析器
                            'my_analyzer'=> [
                                'type' => 'custom',
                                'char_filter' => ['html_strip', '&_to_and'],
                                'tokenizer' => 'standard',  // 标准分词器
                                'filter' => ['lowercase', 'my_stopwords']
                        ]]
                    ]
                    */
                ],
                'mappings' => [ // 映射,相当于表结构
                    '_source' => [
                        'enabled' => true
                   ],
                    'properties' => []
               ]
           ]
        ];
        foreach($body as $key => $val){
            $params['body']['mappings']['properties'][$key] = [
                'type' => $val // 数据类型
            ];
            if(in_array($val, ['text', 'complete'])){
                $params['body']['mappings']['properties'][$key]['analyzer'] = 'standard'; // 标准分析器,也可用自定义分析器 如 reuters
            }
        }
        $response = $this->client->indices()->create($params);
    }
    /*
     * 删除索引
     */
    public function drop($index){
        $response = $this->client->indices()->delete(['index' => $index]);
    }
    /*
     * 索引文档(给索引添加文档)
     */
    public function create(string $index, int $id, array $body = [])
    {
        $params = [
            'index' => $index,
            'id' => $id,
            'body' => $body
       ];
        
        // Document will be indexed to my_index/my_type/my_id
        $response = $this->client->index($params);
    }
    // 搜索
    public function search(string $index, array $querys = []): array{
        $filters = $matchs = [];
        foreach($querys as $row){
            if(in_array($row[1], ['=','in'])){
                $filters[$row[0]] = $row[2];
            }
            if(in_array($row[1], ['like'])){
                $matchs[$row[0]] = $row[2];
            }
        }

        $params = [
            'index' => $index,
            'scroll' => '30s',          // how long between scroll requests. should be small!
            'size'   => 50,             // how many results *per shard* you want back
            'body'  => [
                'query' => [
                    'bool' => [
                        /*
                        'filter' => [  // 过滤器
                            'term' => $filters
                        ],
                        */
                        'should' => [
                            'match' => $matchs, // match 全文搜索
                            // 'term' => ['my_field' => 'abc'],  // term 精确匹配
                            /*
                            'range' => [// range 范围搜索
                                'gte' => 20,  // 大于等于
                                'lte' => 40  // 小于等于
                           ]
                           */
                        ]
                    ]
                ]
            ]
        ];
        $res = $this->client->search($params);
        return $res['hits']['hits'];
        //while (isset($res['hits']['hits']) && count($res['hits']['hits']) > 0) {
        //}
    }
    /*
     * 获取文档
     */
    public function get(string $index, int $id){
        $params = [
            'index' => $index,
            'id'    => $id
       ];

        // Get doc at /my_index/_doc/my_id
        $response = $this->client->get($params);
    }
    /*
     * 更新文档
     */
    public function update(string $index, int $id, array $body = [])
    {
        $params = [
            'index' => $index,
            'id' => $id,
            'body' => [
                // 脚本更新
                'script' => 'ctx._source.counter += count',
                'params' => [
                    'count' => 4
               ],
                // 更新或插入
                'upsert' => [
                    'counter' => 1
               ],
                // 更新文档
                'doc' => [
                    'new_field' => 'abc'
               ]
           ]
       ];

        // Update doc at /my_index/my_type/my_id
        $response = $this->client->update($params);
    }
    /*
     *  删除文档
     */
    public function delete(string $index, int $id)
    {
        $params = [
            'index' => 'my_index',
            'id' => 'my_id'
       ];
        // Delete doc at /my_index/my_type/my_id
        $response = $this->client->delete($params);
    }
    /*
     * 批量索引文档
     */
    public function bulk(string $index, array $data = [], string $pk = 'id'){
        $body = [];
        $max = count($data) - 1;
        foreach ($data as $k => $row) {
            $body[] = [
                'index' => [
                    '_index' => $index,
                    '_id'    => $row[$pk]
               ]
           ];
        
            $body[] = $row;
            // Every 1000 documents stop and send the bulk request
            if ($k > 0 && ($k === $max || $k % 5000 === 0)) {             
                $responses = $this->client->bulk(['body' => $body]);
                $body = [];
            }
        }
    }
}
